package yaodong.utils;

/**
 * Created
 * Author:lvyaodong[Darksider]
 * Email:1126220529@qq.com
 * Date:2015/10/16
 */

import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.InvalidKeySpecException;

/**
 * 常用加密工具类 包含:<br/>
 * <ol>
 *     <li>Hex 编码</li>
 *     <li></li>
 * </ol>
 */
public final class EncryptUtil {
    private EncryptUtil(){

    }

    //-----------------------------------------------------------------------------------
    //RSA 的密钥生成

    /**
     * 通过指定的秘钥长度 生成非对称的密钥对
     *
     * @param keySize 推荐使用1024 2048 不允许低于 1024
     * @return
     */
    public static KeyPair generateRSAKeyPair(int keySize){
        KeyPair ret =null;

        try {
            //1.准备生成
            KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
            //2.初始化 设置keysize
            generator.initialize(keySize);
            //3.生成并返回
            ret = generator.generateKeyPair();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return ret;
    }

    /**
     * RSA 加密
     * @param data
     * @param key 可以是publickey 也可以是 privatekey
     * @return
     */
    public static byte[] rsaEncrypt(byte[] data,Key key){
        byte[] ret = null;
        if(data!=null&&data.length>0&&key!=null){
            //1.创建Cipher 使用RSA

            try {

                Cipher cipher = Cipher.getInstance("RSA");
                //设置key
                cipher.init(Cipher.ENCRYPT_MODE,key);
                ret = cipher.doFinal(data);

            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            }

        }
        return ret;
    }public static byte[] rsaDecrypt(byte[] data,Key key){
        byte[] ret = null;
        if(data!=null&&data.length>0&&key!=null){
            //1.创建Cipher 使用RSA

            try {

                Cipher cipher = Cipher.getInstance("RSA");
                //设置key
                cipher.init(Cipher.DECRYPT_MODE,key);
                ret = cipher.doFinal(data);

            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            }

        }
        return ret;
    }
    //--------------------------------------------------------------------------------------


    //------------------------------------AES带有加密模式的加密解密--------------------------------
    //形成的加密强度更高 需要Iv参数

    /**
     *
     * @param mode
     * @param data
     * @param keyData
     * @param ivData 用于 AES/CBC/PKCS5Padding 这个带有加密模式的算法
     * @return
     */
    private static byte [] aesWithIv(int mode,byte[] data,byte[] keyData,byte[] ivData){
        byte[] ret = null;
            if(data!=null&&data.length>0&&keyData!=null&&keyData.length==16&&ivData!=null&&ivData.length==16){
                //支持加密模式
                // AES/CBC/PKCS5Padding  AES/ECB/PKCS5Padding
                try {


                    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                    //密码部分还是设置成AES
                    SecretKeySpec keySpec = new SecretKeySpec(keyData,"AES");
                    //准备Iv参数 用于支持CBC ECB 模式
                    IvParameterSpec iv = new IvParameterSpec(ivData);

                    //设置密码以及Iv参数
                    cipher.init(mode,keySpec);
                    ret = cipher.doFinal(data);

                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                } catch (NoSuchPaddingException e) {
                    e.printStackTrace();
                } catch (InvalidKeyException e) {
                    e.printStackTrace();
                } catch (BadPaddingException e) {
                    e.printStackTrace();
                } catch (IllegalBlockSizeException e) {
                    e.printStackTrace();
                }
            }
        return ret;

    }
    public static byte[] aesWithIvEncrypt(byte[] data,byte[] keyData,byte[] ivData){
        return aesWithIv(Cipher.ENCRYPT_MODE, data, keyData, ivData);
    }
    public static byte[] aesWithIvDecrypt(byte[] data,byte[] keyData,byte[] ivData){
        return aesWithIv(Cipher.DECRYPT_MODE, data, keyData, ivData);
    }
    //------------------------------------AES加密解密--------------------------------
    public static byte[] aesSingle(int mode,byte[] data,byte[] keyData){
        byte[] ret = null;
        if(data!=null&&data.length>0&&keyData!=null&&keyData.length==16){//128bit AES
            try {

                Cipher cipher = Cipher.getInstance("AES");
                //AES 方式1 单一密码的情况  不同于DES
                SecretKeySpec keySpec = new SecretKeySpec(keyData,"AES");
                cipher.init(mode,keySpec);
                ret = cipher.doFinal(data);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            }

        }
        return ret;
    }
    public static byte[] aesEncrypt(byte[] data,byte[] keyData){
        return aesSingle(Cipher.ENCRYPT_MODE,data,keyData);
    }
    public static byte[] aesDecrypt(byte[] data,byte[] keyData){
        return aesSingle(Cipher.DECRYPT_MODE,data,keyData);
    }

    //------------------------------------AES加密解密--------------------------------

    //-----------------------------------------------------DES对称加密解密------------------------------------------

    public static byte[] des(int mode,byte[] data,byte[] keyData){
        byte[] ret = null;
        if(data!=null&&data.length>0&&keyData!=null&&keyData.length == 8){
            try {

                //创建加密引擎
                Cipher cipher = Cipher.getInstance("DES");
                //3.准备key 对象
                //3.1 DES 使用DESKeySpec  内部构造的时候指定8 个字节的密码即可；
                DESKeySpec desKeySpec = new DESKeySpec(keyData);
                //3.2 DESKeySpec需要转换为 Key 对象 才可以继续使用
                //需要使用SecretKeyFactory来处理
                SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
                //3.3生成Key 对象
                SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
                //2.设置加密引擎是加密还是解密
                //同时对于对称加密需要设置密码 key对象 参数2 是key对象
                cipher.init(mode,secretKey);
                //4.加密 doFinal 方法可以设置字节数组 作为待加密的内容  返回值就是最终的加密结果
                byte[] encryptData = cipher.doFinal(data);
                ret = encryptData;

            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (NoSuchPaddingException e) {
                e.printStackTrace();
            } catch (IllegalBlockSizeException e) {
                e.printStackTrace();
            } catch (BadPaddingException e) {
                e.printStackTrace();
            } catch (InvalidKeySpecException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            }
        }

        return ret;
    }

    //DES 加密
    public static byte[]   desEncrypt(byte[] data,byte[] keyData){
        return des(Cipher.ENCRYPT_MODE,data,keyData);
    }
    //DES 解密
    public static byte[]   desDecrypt(byte[] data,byte[] keyData){
        return des(Cipher.DECRYPT_MODE,data,keyData);
    }
    //--------------------------------------DES加解密----------------------------------------------------
    /**
     *
     * @param data
     * @return
     * 一个字节转换为两个字符   最终长度是2的倍数
     */
    public static String toHex(byte[] data){
        String ret = null;
        //TODO 将字节数组转换为字符串
        if(data!=null&&data.length>0){
            StringBuilder sb = new StringBuilder();
            for(byte b:data){
                int h = (b>>4)&0x0f;
                int l = b&0x0f;

                char ch,cl;

                if(h>9){//0x0a `0x0f
                    ch = (char) ('A'+(h - 10));
                }else {//0-9
                    ch = (char) ('0'+h);
                }

                if(l>9){
                    cl = (char) ('A'+(l-10));
                }else {
                    cl = (char) ('0'+l);
                }
                sb.append(ch).append(cl);
                ret = sb.toString();
            }
        }

        return ret;
    }

    public static byte[] fromHex(String str){
        byte[] ret = null;
        //TODO 将字符串转换为字节数组
        if (str != null) {
            int len = str.length();
            //检查长度是否合法
            if (len > 0 && len % 2 == 0) {
                char[] chs = str.toCharArray();
                ret = new byte[len/2];
                for (int i = 0 ,j = 0; i < len - 1; i += 2,j++) {

                    char ch = str.charAt(i);
                    char cl = str.charAt(i + 1);
                    int ih = 0,il = 0,v = 0;
                    if(ch>='A'&& ch<='F'){
                        ih = 10 + (ch-'A');
                    }else if(ch >= 'a'&&ch<='f'){
                        ih = 10 + (ch - 'a');
                    }else if(ch>='0'&&ch<='9'){
                        ih =ch - '0';
                    }
                    if(cl>='A'&& cl<='F'){
                        il = 10 + (cl-'A');
                    }else if(cl >= 'a'&&cl<='f'){
                        il = 10 + (cl - 'a');
                    }else if(cl>='0'&&cl<='9'){
                        il =cl - '0';
                    }
                    v = ((ih & 0x0f)<<4) | (il & 0x0f);
                    ret[j] = (byte) v;
                }
            }
        }

        return ret;
    }
}
